#!/usr/bin/env ruby
# Copyright (c) 2009, Keith Salisbury (www.globalkeith.com)
# All rights reserved.
# 
# Redistribution and use in source and binary forms, with or without modification,
# are permitted provided that the following conditions are met:
# 
# Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
# 
# Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# 
# Neither the name of the original author nor the names of contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
# 
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
# You may need these...
#
# sudo apt-get install ruby rubygems
# sudo gem sources -a http://gems.github.com
# sudo gem install visionmedia-commander 
# sudo gem install yaml erb fileutils
#

require 'yaml'
require 'erb'
require 'fileutils'
require 'subtrac/version'

module Subtrac
  DEV_ENV = 'test' # should be a system environment var
  DEV_ROOT = Dir.getwd
  module Commands
    class Install
      def initialize(args, options)
        Devserver.load_config()
        Devserver.install(args,options)
      end
    end
    class Create
      def initialize(args, options)
        project = ask("What is the name of the project you would like to create?") if !options.project
        client = ask("Which client is this project for?") if !options.client
        Devserver.load_config()
        Devserver.create_project(options.project,options.client)
      end
    end
  end

  # Install
  def self.install(args,options)
    # Ask if the user agrees (yes or no)
    confirm = agree("Are you sure?") if options.clean
    if confirm
      # clear previous data
      clean()
      create_environment_directories()
    else
      puts "\nAttempt install without overwrites"
    end
    install_common_files()
    create_virtual_host()
    # create default project and client
    #create_client(@APP_CONFIG[:default_client])
    create_project(@APP_CONFIG[:default_project],@APP_CONFIG[:default_client])
  end

  private
  
  # Loads the configuration YML file
  def self.load_config
    puts "\n==== Loading configuration file ===="
    # load configuration file
    begin
      yamlFile = YAML.load_file("#{DEV_ROOT}/config/config.yml")
    rescue Exception => e
      raise StandardError, "config/config.yml could not be loaded."
    end
    if yamlFile
      if yamlFile[DEV_ENV]
        @APP_CONFIG = yamlFile[DEV_ENV]
      else
        raise StandardError, "config/config.yml exists, but doesn't have a configuration for DEV_ENV=#{DEV_ENV}."
      end
    else
      raise StandardError, "config/config.yml does not exist."
    end
  end

  # creates a directory if it does not exist
  def self.create_if_missing *names 
    puts "\n==== Creating directory #{names} ===="
    names.each do |name| FileUtils.mkdir_p(name) unless File.directory?(name) end 
  end

  # publishes an erb template
  def self.parse_template(infile,outfile,binding)
    file = File.new(outfile, "w")
    if file
       file.syswrite(ERB.new(IO.read(infile)).result(binding))
    else
       puts "Unable to write to file #{outfile}!"
    end
  end

  # creates a new virtual host and reloads apache and enables the new virtual host
  def self.create_virtual_host
    puts "\n==== Creating new virtual host ===="
    parse_template(@APP_CONFIG[:templates][:virtual_host],"#{@APP_CONFIG[:dirs][:apache2_conf]}/#{@APP_CONFIG[:server][:hostname]}",binding)
    # reload apache configuration
    `/etc/init.d/apache2 force-reload` if DEV_ENV != 'test'
    `a2ensite #{@APP_CONFIG[:server][:hostname]}` if DEV_ENV != 'test'
  end
  
  def self.install_common_files
    puts "\n==== Installing common files ===="
    # TODO: implement a mash for .svn folders
    # TODO: refactor /common to the app config
    source = Dir.glob("#{DEV_ROOT}/common/.")
    dest = @APP_CONFIG[:dirs][:document]
    FileUtils.cp_r(source,dest)
  end

  def self.create_environment_directories
    puts "\n==== Creating new environment directories ===="
    # create the environment directories
    @APP_CONFIG[:dirs].each do |key, value|
      create_if_missing value
    end
  end  

  def self.clean
    puts "\n==== Remove old data if exists ===="
    @APP_CONFIG[:dirs].each do |key, value|
      unless [@APP_CONFIG[:dirs][:install]].include? value
        #TODO: Ask for confirmation - yes,no,All
        puts "Deleting #{value}"
        FileUtils.rm_rf value
      end
    end
  end
  
  def self.create_client(name)
    puts "\n==== Create a new client #{name} ===="
    client_name = name.downcase
    # create apache configuration
    # create apache template
    parse_template(@APP_CONFIG[:templates][:location],"#{@APP_CONFIG[:dirs][:conf_locations]}/#{client_name}.conf",binding)
    `/etc/init.d/apache2 force-reload` if DEV_ENV != 'test'
    
    # create svn+trac directory
    create_if_missing "#{@APP_CONFIG[:dirs][:svn]}/#{client_name}"
    create_if_missing "#{@APP_CONFIG[:dirs][:trac]}/#{client_name}"
  end
  
  def self.create_project(project, client)
    puts "\n==== Create a new project #{project} for #{client} ===="
    
    client_name = client.downcase
    project_name = project.downcase
    
    # create client directory if needed
    create_client(client_name) if (!File.directory? "#{@APP_CONFIG[:dirs][:svn]}/#{client_name}")
    
    svn_dir = "#{@APP_CONFIG[:dirs][:svn]}/#{client_name}/#{project_name}"
    trac_dir = "#{@APP_CONFIG[:dirs][:trac]}/#{client_name}/#{project_name}"
    if (File.directory? svn_dir) then
      raise StandardError, "A project called #{project} already exists in the #{client} repository. Would you like to replace it?"
    end
    
    # copy template svn project to a temp folder, then svn import it into the repo
    svn_template_dir = "#{@APP_CONFIG[:templates][:blank]}/svn/."
    temp_dir = "#{@APP_CONFIG[:dirs][:temp]}/#{project_name}"
    FileUtils.cp_r(svn_template_dir,temp_dir)
    
    # create a new subversion repository
    `svnadmin create #{svn_dir}` if DEV_ENV != 'test'
    
    # import into svn and delete the temporary directory
    `svn import #{temp_dir} file:///#{svn_dir} --message "initial import"` if DEV_ENV != 'test'
    FileUtils.rm_r(temp_dir, :force => true)
    
    # create a new trac site
    `trac-admin #{trac_dir} initenv #{project_name} sqlite:#{trac_dir}/db/trac.db svn #{svn_dir}` if DEV_ENV != 'test'
    FileUtils.chown_R('www-data', 'www-data', trac_dir, :verbose => true)  if DEV_ENV != 'test'
    FileUtils.mkdir_p("#{trac_dir}/conf") if DEV_ENV == 'test' # fake the folder for tests
    
    # install shared trac.ini
    parse_template(@APP_CONFIG[:templates][:trac],"#{trac_dir}/conf/trac.ini",binding)
    
    # remove custom templates directory so trac uses the shared location (while we wait for trac patch)
    FileUtils.rm_rf("#{trac_dir}/templates")

    puts "Setting up default trac permission..."
    # set up trac permissions
    @APP_CONFIG[:trac][:permissions].each do |key, value|
      `trac-admin #{trac_dir} permission add #{key} #{value}` if DEV_ENV != 'test'
    end

    puts "Adding default trac wiki pages..."
    # this needs to loop through the directory and import all pages
    Dir.foreach("#{DEV_ROOT}/#{@APP_CONFIG[:default_project_template]}/trac/wiki/.") do |file|
      # do something with the file here
      unless ['.', '..','.svn'].include? file
        parse_template("#{DEV_ROOT}/#{@APP_CONFIG[:default_project_template]}/trac/wiki/#{file}","#{@APP_CONFIG[:dirs][:temp]}/#{file}", binding)
        `trac-admin #{trac_dir} wiki import #{file} #{@APP_CONFIG[:dirs][:temp]}/#{file}` if DEV_ENV != 'test'
        FileUtils.rm("#{@APP_CONFIG[:dirs][:temp]}/#{file}")
      end
    end
  end
  
end



